/*
 * This file (AkMainFrame.java) is protected by the GNU GPL licence (v2). 
 * Please read the license.txt attached for further informations. 
 * 
 */

package alkwarel.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;

import alkwarel.objects.AkActor;
import alkwarel.objects.AkArtspace;
import alkwarel.utils.AkConstants;
import artx.events.AxLogEvent;
import artx.gui.AxActorsDlg;
import artx.gui.AxFileChooserFilter;
import artx.gui.AxLogDlg;
import artx.gui.AxParamsDlg;
import artx.listeners.AxLogListener;
import artx.objects.AxManager;
import artx.utils.AxLog;

// TODO add a full history gui

/**
 * Main frame of the Alkwarel GUI
 * 
 * @author Xavier Gouchet
 * 
 */
public class AkMainFrame extends JFrame implements AkConstants, ActionListener,
		KeyListener, AxLogListener {

	private static final long serialVersionUID = 1L;

	// GUI Elements
	private JPanel mainPanel = null;

	private JToolBar toolBar = null;

	private JTextField outputField = null;

	private JTextField inputField = null;

	private AkArtspace artspaceFrame = null;

	// Console Elements
	private ArrayList<String> consoleHistory = null;

	private int consoleCurrent = -1;

	// Alkwarel elements
	private AxManager actorsManager = null;

	/**
	 * Standard constructor.
	 * 
	 */
	public AkMainFrame() {
		super();
		AxLog.Debug("AkMainFrame::AkMainFrame =>> Creating main frame");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setTitle(FULL_TITLE);
		this.setResizable(true);

		// Set Content
		this.setContentPane(this.GetMainPanel());
		AxLog.AddLogListener(this);
		this.pack();
		AxLog.Debug("AkMainFrame::AkMainFrame =>> Frame created");

		this.actorsManager = new AxManager(DIR_CONFIG+"actors.list",
				AkActor.class, "alkwarel.actors");

	}

	/**
	 * If the main panel doesn't exist, build it and return it.
	 * 
	 * @return the main panel
	 */
	public JPanel GetMainPanel() {
		if (this.mainPanel == null) {
			AxLog.Debug("AkMainFrame::GetMainPanel =>> Building frame content");
			// create Panel
			mainPanel = new JPanel();
			mainPanel.setLayout(new GridBagLayout());
			GridBagConstraints gbc = new GridBagConstraints();

			// add toolbar
			gbc.gridx = 0;
			gbc.gridy = 0;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			gbc.insets = new Insets(2, 2, 2, 2);
			mainPanel.add(this.GetToolBar(), gbc);

			// add Artspace (P55)
			artspaceFrame = new AkArtspace();
			artspaceFrame.setPreferredSize(new Dimension(640, 480));
			artspaceFrame.setMinimumSize(new Dimension(640, 480));
			artspaceFrame.setMaximumSize(new Dimension(640, 480));
			artspaceFrame.init();
			artspaceFrame.useGui = true;
			gbc.gridx = 0;
			gbc.gridy = 1;
			gbc.fill = GridBagConstraints.NONE;
			mainPanel.add(artspaceFrame, gbc);

			// add console output
			outputField = new JTextField();
			outputField.setEditable(false);
			outputField.setBackground(new Color(192, 192, 192));
			gbc.gridy = 2;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			mainPanel.add(outputField, gbc);

			// add input text field
			inputField = new JTextField();
			inputField.addActionListener(this);
			gbc.gridy = 3;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			inputField.addKeyListener(this);
			mainPanel.add(inputField, gbc);

		}
		return this.mainPanel;
	}

	/**
	 * If the toolbar doesn't exist, build it and return it.
	 * 
	 * @return the toolbar
	 */
	public JToolBar GetToolBar() {
		if (toolBar == null) {
			AxLog.Debug("AkMainFrame::GetToolBar =>> Building tool bar");
			// create toolbar
			toolBar = new JToolBar("Alkwarel Tool Box");
			this.AddToolBarButton("new.png", "new", "New");
			//this.AddToolBarButton("save.png", "save", "Save");
			this.AddToolBarButton("export.png", "export", "Export");

			toolBar.addSeparator();
			//this.AddToolBarButton("actors.png", "actors", "Actors");
			this.AddToolBarButton("colors.png", "colors", "Colors");
			this.AddToolBarButton("tools.png", "tools", "Tools");
			toolBar.addSeparator();
			this.AddToolBarButton("params.png", "params", "Parameters");
			//this.AddToolBarButton("dico.png", "dico", "Dictionnary");
			this.AddToolBarButton("random.png", "random", "Random");
			this.AddToolBarButton("log.png", "log", "Log");
			toolBar.addSeparator();
			this.AddToolBarButton("about.png", "about", "About");
			toolBar.setFloatable(false);
			toolBar.setRollover(true);
		}
		return toolBar;
	}

	/**
	 * Adds a new button to the toolbar.
	 * 
	 * @param image
	 * @param action
	 * @param text
	 */
	public void AddToolBarButton(String image, String action, String text) {
		AxLog.Debug("AkMainFrame::AddToolBarButton =>> Creating button \""
				+ action + "\"");
		String imgLocation = DIR_ICONS + image;

		JButton button = new JButton();
		button.setActionCommand(action);
		button.addActionListener(this);
		button.setMargin(new Insets(1, 1, 1, 1));
		button.setBorder(null);
		
		ImageIcon icn = new ImageIcon(imgLocation);
		button.setIcon(icn);

		toolBar.add(button);
	}

	/**
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		AxLog.Debug("AkMainFrame::actionPerformed =>> Event callback : "
				+ e.getActionCommand());

		if (e.getSource() == this.inputField) {
			String text = e.getActionCommand();
			this.InputCommand(text);
			return;
		}

		if (e.getSource().getClass() == JButton.class) {
			if (e.getActionCommand().equals("new")) {
				this.NewArtspace();
				return;
			}

			if (e.getActionCommand().equals("save")) {
				this.SaveArtspace();
				return;
			}

			if (e.getActionCommand().equals("export")) {
				this.ExportArtspace();
				return;
			}

			if (e.getActionCommand().equals("colors")) {
				this.artspaceFrame.GetColorManager().ShowGUIDlg();
				return;
			}

			if (e.getActionCommand().equals("log")) {
				JDialog logDlg = new AxLogDlg(this, TITLE + " Log Console");
				logDlg.setVisible(true);
				return;
			}
			
			if (e.getActionCommand().equals("random")) {
				this.artspaceFrame.ApplyRandomInstruction();
				return;
			}

			if (e.getActionCommand().equals("actors")) {
				AxActorsDlg actorsDlg = new AxActorsDlg(this, TITLE
						+ " - Actors", this.actorsManager);
				actorsDlg.setModalityType(ModalityType.APPLICATION_MODAL);
				actorsDlg.setVisible(true);
				// TODO launch the actor (actorsDlg.selected)
				return;
			}

			if (e.getActionCommand().equals("params")) {
				JDialog paramsDlg = new AxParamsDlg(this, TITLE
						+ " - Artspace Parameters", this.artspaceFrame
						.GetParameterManager());
				paramsDlg.setModalityType(ModalityType.APPLICATION_MODAL);
				paramsDlg.setVisible(true);
				return;
			}

			if (e.getActionCommand().equals("tools")) {
				JDialog toolsDlg = new AkToolsDlg(this, TITLE
						+ " - Artspace Tools", this.artspaceFrame);
				toolsDlg.setModalityType(ModalityType.MODELESS);
				toolsDlg.setVisible(true);
				return;
			}

			if (e.getActionCommand().equals("about")) {
				JDialog aboutDlg = new AkAboutDlg(this);
				aboutDlg.setVisible(true);
				return;
			}

		}
	}

	/**
	 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
	 */
	public void keyPressed(KeyEvent e) {
		if (consoleHistory == null)
			return;
		switch (e.getKeyCode()) {
		case 38: // UP
			if (consoleCurrent > 0) {
				consoleCurrent -= 1;
				if (consoleCurrent < consoleHistory.size()) {
					inputField.setText(consoleHistory.get(consoleCurrent));
				}
			}
			break;
		case 40: // Down
			if (consoleHistory != null) {
				if (consoleCurrent + 1 < consoleHistory.size()) {
					consoleCurrent += 1;
					if (consoleCurrent >= 0) {
						inputField.setText(consoleHistory.get(consoleCurrent));
					}
				} else {
					if (consoleCurrent + 1 == consoleHistory.size()) {
						consoleCurrent += 1;
						inputField.setText("");
					}
				}
			}
			break;
		default:
			break;
		}
	}

	/**
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	public void keyReleased(KeyEvent e) {
	}

	/**
	 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
	 */
	public void keyTyped(KeyEvent e) {
	}

	/**
	 * @see artx.listeners.AxLogListener#messageRegistered(artx.events.AxLogEvent)
	 */
	public void messageRegistered(AxLogEvent evt) {
		switch (evt.GetLevel()) {
		case LVL_ERROR:
			this.outputField.setForeground(new Color(192, 0, 0));
			break;
		case LVL_WARNING:
			this.outputField.setForeground(new Color(192, 96, 0));
			break;
		default:
			this.outputField.setForeground(new Color(0, 0, 0));
			break;
		}
		this.outputField.setText(evt.GetMessage());
	}

	/**
	 * @see artx.listeners.AxLogListener#debugMessageRegistered(artx.events.AxLogEvent)
	 */
	public void debugMessageRegistered(AxLogEvent evt) {
	}

	private void NewArtspace() {
		AxLog.Debug("AkMainFrame::NewArtspace =>> Checking current artspace");
		// test if current artspace is unsaved
		if ((this.artspaceFrame != null) && (this.artspaceFrame.IsUnsaved())) {
			int save = JOptionPane.showOptionDialog(null,
					"The current artspace has been modified. Do you want to "
							+ "save it before creating a new artspace? ",
					"Alkwarel - Warning", JOptionPane.YES_NO_CANCEL_OPTION,
					JOptionPane.WARNING_MESSAGE, null, new Object[] { "Yes",
							"No", "Cancel" }, "Cancel");
			switch (save) {
			case 0:
				this.SaveArtspace();
				System.err.println("Save and new");
				break;
			case 2:
				System.err.println("Cancel");
				return;
			}
		}
		// create new artspace
		AxLog.Debug("AkMainFrame::NewArtspace =>> Creating new artspace");
		this.artspaceFrame.Reset();
		AxLog.Info("New Artspace created");
	}

	private void SaveArtspace() {
		AxLog.Debug("AkMainFrame::SaveArtspace =>> Saving Artspace");
		JFileChooser fc = new JFileChooser();
		fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		fc.setMultiSelectionEnabled(false);
		ArrayList<String> filters = new ArrayList<String>();
		filters.add("aka");
		fc.setFileFilter(new AxFileChooserFilter("Alkwarel Artspace (*.aka)",
				filters));
		if (fc.showDialog(null, "Save") == 0) {
			String path = fc.getSelectedFile().getAbsolutePath();
			AxLog.Debug("AkMainFrame::SaveArtspace =>> Saving to path " + path);
			artspaceFrame.SaveArtspace(path);
			AxLog.Info("Artspace saved to " + path);
		}
		// TODO keep last directory as default saving location
	}

	private void ExportArtspace() {
		AxLog.Debug("AkMainFrame::ExportArtspace =>> Exporting Artspace");
		JFileChooser fc = new JFileChooser();
		fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		fc.setMultiSelectionEnabled(false);
		ArrayList<String> filters = new ArrayList<String>();
		filters.add("tiff");
		filters.add("tif");
		filters.add("png");
		filters.add("tga");
		filters.add("jpg");
		filters.add("jpeg");
		fc.setFileFilter(new AxFileChooserFilter(
				"Images (*.tif;*.png;*.tga;*.jpg)", filters));
		if (fc.showDialog(null, "Export") == 0) {
			String path = fc.getSelectedFile().getAbsolutePath();
			AxLog.Debug("AkMainFrame::ExportArtspace =>> Exporting to path "
					+ path);
			artspaceFrame.ExportArtspace(path);
			AxLog.Info("Picture saved to " + path);
		}
		// TODO keep last directory as default exporting location
	}

	private void InputCommand(String text) {
		if (!text.isEmpty()) {
			AxLog.Info(text);
			if (consoleHistory == null) {
				consoleHistory = new ArrayList<String>();
			}
			if (consoleHistory.size() > 0) {
				if (!text.equals(consoleHistory.get(consoleHistory.size() - 1))) {
					consoleHistory.add(text);
				}
			} else {
				consoleHistory.add(text);
			}
			consoleCurrent = consoleHistory.size();
			inputField.setText("");
			if (this.artspaceFrame != null) {
				this.artspaceFrame.ParseCommand(text.toLowerCase());
			}
		}
	}

}
